/*
 * 旨在消灭项目中重复的JS代码;
   核心思想是:约定优于配置.
   即在元素上做特别的约定(标识)就可以实现通用的功能,而不需要额外的通过js代码去实现这些功能.
   文件划分:
   utf8mb4_general_ci.js 文件: 理应做为做基本的文件,实现最通用的功能,理应每个项目都可以无缝引入的功能,理应放在head中引入
   utf8mb4_general_ci-module-moduleName.js 文件: 主要为某一些项目某些某块特定处理的功能,分多文件,按需引入
   @author Vic.xu
   @since terrason 
 */
(function (windom, $) {
    /*
     * 在nojs.init执行之前执行的函数 在模块加载前运行代码 不要放在ready中
      使用方式: ($nojs.after同理)
      $nojs.before(function(context){
          this; //表示nojs全局对象，等同于$nojs.
          this.test.enable=false;    //禁用test模块
          this.chosen.enable=false;   //禁用chosen模块
          //do something else
      });
     *
    */
    var before = [];
    var after = [];
    window.base_url = windom.base_url || "";
    windom.storage = windom.storage || {
        get: function () {
        }
    };
    windom.project_prefix = windom.project_prefix || "/";
    var $nojs = function (context) {
        //在初始化nojs的各个模块之前执行的方法
        for (var i in before) {
            if ($.isFunction(before[i])) {
                if (before[i].call($nojs, context) === false) {
                    return;
                }
            }
        }
        //针对模块的优先级进行一次排序操作   priority  越小  越先执行
        var modules = [];
        var priorityDefault = 99; //默认的优先级
        for (var name in $nojs) {
            var app = $nojs[name];
            if (!app.priority && 0 != app.priority) {
                app.priority = priorityDefault;
            }
            app.name = name;
            modules.push(app);
        }
        modules.sort(function (p1, p2) {
            return p1.priority - p2.priority;
        }).forEach(function (app) {
            if ($.isFunction(app.enable) ? app.enable() : app.enable === true) {
                if ($nojs.debug) {
                    console.debug(app.name + " ->" + app.priority)
                }
                app.init(context);
            }
        });
        //在初始化nojs的各个模块之后执行的方法
        for (var i in after) {
            $.isFunction(after[i]) && after[i].call($nojs, context);
        }
    };
    $nojs.before = function (fn) {
        before.push(fn);
    }
    $nojs.after = function (fn) {
        after.push(fn);
    }
    $nojs.debug = false;

    /*
     * 000. 测试 所有约定的写法遵照此格式写法
     */
    $nojs.test = {
        priority: 100,
        // 1-是否启用此约定,可以根据依赖模块判断或直接返回 boolean
        enable: function (context) {
            return true;
        },
        //2-选择器,对哪些元素做一些操作
        selector: ".test",
        //3-对元素做哪些具体的操作
        init: function (context) {
            $(this.selector, context).each(function (i, t) {
                $(t).append("<h1>这是一个测试</h1>")
            });
        }
    };
    /*
     * 001. select值绑定
     * 自动选择下拉框的值, 需要在下拉框上绑定当前值 data-value="value"
     */
    $nojs.selectValueInitialization = {
        enable: true,
        selector: "select[data-value]",
        priority: 98,//保证比select2之类的先初始化
        event: "$nojs-select-initialize",
        init: function (context) {
            $(this.selector, context).on(
                this.event,
                function (event) {
                    var $select = $(this);
                    var value = $select.data("value");
                    if (value !== undefined) { // 把相应的值选中
                        $select.children("option:selected").prop("selected", false);
                        if (value !== '') {
                            if (!$select[0].hasAttribute("multiple")) {
                                $select.children('option[value="' + value + '"]').prop("selected", true);
                            } else {//如果是多选则把value用英文逗号分隔后匹配选择
                                value.toString().split(",").forEach(function (item) {
                                    $select.children('option[value="' + item + '"]').prop("selected", true);
                                });
                            }
                        }
                    }
                    $select.trigger("change");
                }).trigger(this.event);
        }
    };

    /*
     * 002. 日历控件
        把datetime样式的元素初始化成bootstrap的日期控件
     */
    $nojs.datetimepicker = {
        enable: function () {
            console.info(!!$.fn.datetimepicker);
            return !!$.fn.datetimepicker;
        },
        options: {
            autoclose: true,
            todayBtn: true,
            language: "zh-CN"
        },
        selector: ".datetime",
        initRange: function (option, $input, context) {


            function _bindDateTarget(opt) {
                var method = {
                    "startDateTarget": "setStartDate",
                    "endDateTarget": "setEndDate"
                };
                if (option[opt] === 'now') {
                    $input.datetimepicker(method[opt], new Date());
                    return;
                }
                var $target = $(option[opt], context);
                if (!$target.length) {
                    $target = $("[name=" + option[opt] + "]", context);
                }
                $target.on("changeDate", function (ev) {
                    $input.datetimepicker(method[opt], this.value);
                });
            }

            if (!option.startDateTarget && !option.endDateTarget) {
                return;
            }
            var $form = $input.closest("form");
            if ($form.length) {
                context = $form;
            }

            if (option.startDateTarget) {
                _bindDateTarget("startDateTarget");
            }
            if (option.endDateTarget) {
                _bindDateTarget("endDateTarget");
            }
        },
        init: function (context) {
            var module = this;
            $(this.selector, context).each(function (i, dt) {
                var $input = $(dt);
                var option = $.extend({}, module.options, $input.data());

                $input.datetimepicker(option);
                module.initRange(option, $input, context);
                if (option.value) {
                    $input.val(option.value);
                }
                $input.next("span").click(function () {
                    $input.focus();
                });
            });
        }
    };

    /*
     * 004. 日历
     * 把calendar样式的div元素初始化成bootstrap的日期控件
     */
    $nojs.timeCalendar = $.extend(true, {}, $nojs.datetimepicker, {
        selector: "div.calendar"
    });


    /*
     * 005. CheckBox 和全选/取消 和相关批量操作按钮 的关系建立
     *  1-全选按钮data-member="subName"  和 name="subName" 的元素关联起来
     *  2-操作按钮 data-checkbox-required="subName" 是否可以操作(是否选择name="subName" 子元素)
     */
    $nojs.checkboxBinding = {
        enable: true,
        selector: "[data-member]",
        $member: function ($leader, context) { // 获取成员
            var memberName = $leader.data("member"); // 成员name
            // return $("input:checkbox[name=" + memberName + "]:not(:disabled)",	context);
            return $("input:checkbox[data-leader=" + memberName + "]:not(:disabled)", context);
        },
        $relate: function ($leader, $member, context) { // 相关操作按钮是否可操作
            var memberName = $leader.data("member"); // 成员name
            var $relate = $("[data-checkbox-required=" + memberName + "]",
                context)
            if ($relate.length !== 0) {
                $relate.prop("disabled",
                    $member.filter(":checked").length === 0);
            }
        },
        init: function (context) {
            var module = this;
            $(this.selector, context).each(function () {
                var $leader = $(this);
                var $member = module.$member($leader, context);
                $leader.change(function () { // 点击全选或者取消
                    $member.prop("checked", $leader.prop("checked"));
                    module.$relate($leader, $member, context);
                });
                $member.change(function () { // 点击成员 若全选则全选checkbox选中
                    $leader.prop("checked", $member.length == $member
                        .filter(":checked").length);
                    module.$relate($leader, $member, context);
                });
                module.$relate($leader, $member, context);
            });
        }
    };

    /*
     * 006.chosen 下拉框初始化
     */
    $nojs.chosen = {
        enable: function () {
            return !!$.fn.chosen;
        },
        selector: "select.chosen",
        option: {
            no_results_text: "没有找到", // 找不到结果时候显示的内容
            allow_single_deselect: true, // 是否允许取消选择
            max_selected_options: 12
            // 当select为多选时，最多选择个数
        },
        init: function (context) {
            var module = this;
            $(module.selector, context).each(function () {
                var $select = $(this);
                var opts = $.extend(module.option, $select.data());
                $select.chosen(opts);
            });
        }
    };

    /*
     * 007. 发送短信验证码
     * data-smscode 的按钮点击产生倒计时效果
     */
    $nojs.smscode = {
        enable: function () {
            return !!$.timer;
        },
        selector: "button[data-smscode],a[data-smscode]",
        init: function (context) {
            var thisModule = this;
            // 短信验证码

            $(document).on("click", thisModule.selector, function () {
                var $this = $(this);
                var originalCaption = $this.text();
                var $caption = $("<span></span>");
                var remain = 60;
                var $remain = $("<span></span>").text(remain);
                $caption.append($remain).append(" 秒后才可重新获取");
                $this.html($caption);
                var timer = $.timer(function () {
                    if (remain > 0) {
                        $remain.text(--remain);
                    } else {
                        timer.stop();
                        $(thisModule.selector, context).prop(
                            "disabled", false);
                        $this.text(originalCaption);
                    }
                }, 1000, true);
                $(thisModule.selector, context).prop("disabled", true);
            });
        }
    };

    /**
     * 008.icon-picker初始化
     */
    $nojs.iconPicker = {
        enable: function () {
            return !!$.fn.iconPicker;
        },
        selector: 'input.icon-picker',
        init: function (context) {
            $(this.selector, context).iconPicker();
        }
    };

    /**
     * 009. 异步构建一棵树  通过ztree
     * url:  data-url="" 数据加载的url
     * setting : data-setting='{"k1":"v1", "k2":"v2"}' 用data绑定json数据: 外部单引号 内部双引号; 也可以是一个变量(不加引号)
     * after:tree初始化后执行的方法,参数为当前树data-after="fnName"
     * checked:绑定当前应该选中的节点 data-checked="idValue"
     * http://www.treejs.cn/v3/api.php
     */
    $nojs.buildZtree = {
        enable: function () {
            return !!$.fn.zTree;
        },
        selector: 'ul.ztree:not(.self)',
        defaultZtreeSetting: { //默认的ztree设置
            view: {
                showLine: true,
                selectedMulti: false
            },
            data: {
                simpleData: {
                    enable: true,
                    idKey: "id",
                    pIdKey: "pid",
                    rootPId: "0"
                },
                key: {
                    url: "_url" //把url设置成一个不存在的key
                }
            }
        },
        //把setting中的一些方法对应的字符串解析成function
        settingString2Fn: function (opts) {
            var callback = opts.callback;
            //把callback中的字符串解析成函数
            if (callback) {
                for (var k in callback) {
                    callback[k] = eval(callback[k]);
                }
            }
            //把callviewback中的字符串解析成函数 因为只有部分是function,因此用-fn作为后缀的才表示是函数
            var view = opts.view;
            if (view) {
                for (var k in view) {
                    if (k.indexOf('-fn') > -1) {
                        var v = view[k];
                        var index = k.indexOf('-fn');
                        var newK = k.substring(0, index);
                        view[newK] = eval(v); //加入新的key
                        delete view[k]; //删除老的key

                    }
                    callback[k] = eval(callback[k]);
                }
            }
        },
        init: function (context) {
            var module = this;
            $(module.selector, context).each(function () {
                var $ul = $(this);
                var url = $ul.data("url");
                if (!url) return;
                var setting = eval($ul.data("setting")) || {};
                module.settingString2Fn(setting);
                var afterFn = $ul.data("after");
                var checked = $ul.data("checked");
                $request.get(url, {}, function (result) {
                    setting = $.extend(true, module.defaultZtreeSetting, setting);
                    var zTreeObj = $.fn.zTree.init($ul, setting, result.data);
                    $ul.data("ztree", zTreeObj);
                    if (checked) { //把需要选中的节点选中
                        zTreeObj.selectNode(zTreeObj.getNodeByParam("id", curId, null));
                    }
                    if (afterFn) { //初始化完成执行 的函数
                        common.callFunction(afterFn, zTreeObj);
                    }
                });
            });
        }
    };

    /**
     * 010. select2 初始化
     * https://select2.org/
     */
    $nojs.select2 = {
        enable: function () {
            return !!$.fn.select2;
        },
        selector: 'select.select2',
        options: {
            allowClear: true,
            language: 'zh-CN'
        },
        init: function (context) {
            var module = this;
            $(module.selector, context).each(function () {
                var $select = $(this);
                var opts = $.extend(true, {}, module.options, $select.data());
                $select.select2(opts);
            });
        }
    };

    /**
     * 011. 滑块 尽量使用data绑定参数
     * http://ionden.com/a/plugins/ion.rangeSlider/start.html
     */
    $nojs.slider = {
        enable: function () {
            return !!$.fn.ionRangeSlider;
        },
        selector: "input.slider",
        init: function (context) {
            $(this.selector, context).each(function () {
                var $input = $(this);
                $input.ionRangeSlider();
            });
        }
    };

    /**
     * 012. bootstrap-select https://www.bootstrapselect.cn/index.htm
     * selectpicker
     */
    $nojs.selectpicker = {
        enable: function () {
            return !!$.fn.selectpicker;
        },
        selector: "select.selectpicker", // 加此样式 会被插件本身初始化, 但是由于一些数据的加载顺序 此处重新初始化一次
        init: function (context) {
            $(this.selector, context).each(function () {
                $(this).selectpicker();
            });
        }
    };

    /*
    *013. wangEditor 编辑器初始化
    * 把textarea 隐藏,然后初始化一个wangEditor编辑器,并监听编辑器 把编辑器的内容同步到textarea
    */
    $nojs.wangEditor = {
        enable: function () {
            return !!window.wangEditor;
        },
        selector: "textarea.wangEditor-textarea",
        template: {
            toolbar: '<div class="wangEditor-toolbar">',//toolbar
            editor: '</div><div class="wangEditor-text">'//编辑区
        },
        customConfig: { // wangEditor的一些其他配置  此处主要配置 文件上传相关  其他一般走默认
            pasteFilterStyle: true, // 粘贴样式的过滤 默认 true
            pasteIgnoreImg: false, // 忽略粘贴内容中的图片 默认是false
            pasteTextHandle: function () {
            }, //自定义处理粘贴的文本内容
            uploadImgShowBase64: false, // 使用 base64 保存图片
            uploadImgServer: base_url + '/attachment/upfiles', // 上传图片到服务器
            uploadFileName: 'upfiles', //上传文件的name属性
            uploadImgParams: { //额外参数
                module: 'wangEditor'
            },
            uploadImgHeaders: { //header token 检验
                token: storage.get("token")
            },
            uploadImgHooks: { //钩子函数
                //当服务器返回格式非 {errno:0, data: [...]}
                customInsert: function (insertImg, result, editor) {
                    // 图片上传并返回结果，自定义插入图片的事件（而不是编辑器自动插入图片！！！）
                    // insertImg 是插入图片的函数，editor 是编辑器对象，result 是服务器端返回的结果
                    console.info(result);
                    if (result.code != 0) {
                        $.alert(result.msg, "上传出错了");
                        return;
                    }
                    if (result.data) {
                        result.data.forEach(function (item) {
                            insertImg(item.url);
                        });
                    }

                }
            }
        },
        init: function (context) {
            var module = this;
            $(this.selector, context).each(function (i, t) {
                var $textarea = $(this);
                var $toolbar = $(module.template.toolbar).attr("id", "wang-toolbar-" + i);
                var $editor = $(module.template.editor).attr("id", "wang-editor-" + i);
                $textarea.after($editor).after($toolbar).hide();
                // $textarea 上携带的一些自定义参数
                var dataOpt = $textarea.data();
                var opt = $.extend({}, module.customConfig, dataOpt);
                var E = window.wangEditor;
                var editor = new E($toolbar[0], $editor[0])
                editor.customConfig = opt;
                editor.customConfig.onchange = function (html) {
                    // 监控变化，同步更新到 textarea
                    $textarea.val(html);
                }
                editor.create();
                //设置初始值 为$textarea中的值
                editor.txt.html($textarea.val());

            });
        }
    };
    /**
     * 014. editor.md markdown编辑器初始化
     *  把一个div.editormd 初始化位一个markdown编辑器, 这个div中必须包含一个且只能是一个textarea
     *  这个div 可通过data绑定一些editor.md的配置,
     *  初始化成功后把editormd 绑定到这个div上, 方便后续使用
     */
    $nojs.editormd = {
        enable: function () {
            return !!window.editormd;
        },
        selector: "div.editormd",
        config: {
            width: "100%",
            height: 640,
            autoHeight: false,
            watch: true,
            toolbarAutoFixed: true,
            tocm: true,
            emoji: true,
            path: project_prefix + "/assets/lib/editor.md-master/lib/",
            saveHTMLToTextarea: false, // 保存 HTML 到 Textarea
            imageUpload: true,
            imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
            imageUploadURL: base_url + "/attachment/mdload",
            /* toolbarIcons : function() {
                // Or return editormd.toolbarModes[name]; // full, simple, mini
                // Using "||" set icons align right.
                return ["undo", "redo", "bold","del", "italic", "quote", "uppercase",
                "h1", "h2","h3","h4","h5","h6",
                "list-ul", "list-ol","hr", "link", "reference-link", "image", "code",
                "preformatted-text", "code-block", "table", "datetime","emoji","html-entities",
                "pagebreak","goto-line", "watch" ,"unwatch", "preview", "search", "fullscreen",
                "clear","help","info"
                ];
            }, */
        },
        init: function (context) {
            var module = this;
            $(this.selector, context).each(function (i) {
                var $div = $(this);
                if ($("textarea", $div).size() != 1) {
                    return true; // continue
                }
                var id = $div.attr("id") || "editormd-" + i;
                $div.attr("id", id);
                var editor = editormd(id, $.extend({}, module.config, $div.data()));
                $div.data("editormd", editor);
            });
        }
    };

    /**
     * 015. editor.md markdown 转html
     */
    $nojs.mdToHtml = {
        enable: function () {
            return !!window.editormd;
        },
        selector: "div.showmd",
        config: {
            markdown: '',//markdown 内容
            tocContainer: "#toc", // 自定义 ToC 容器层
            //htmlDecode      : true,       // 开启 HTML 标签解析，为了安全性，默认不开启
            htmlDecode: "style,script,iframe", // you can filter tags decode
            //toc             : false,
            tocm: true, // Using [TOCM]
            path: "assets/lib/editor.md-master/lib/lib/",

            //gfm             : false,
            //tocDropdown     : true,
            // markdownSourceCode : true, // 是否保留 Markdown 源码，即是否删除保存源码的 Textarea 标签
            emoji: true,
            taskList: true,
            tex: true, // 默认不解析
            flowChart: true, // 默认不解析
            sequenceDiagram: true, // 默认不解析
        },
        //目录的html
        tocHtml:
            `<div class="panel panel-default md-toc">
			   <div class="panel-heading">
				   <h3 class="panel-title">
					   <span>目录</span><a id="closeToc"><i class="glyphicon glyphicon-remove pull-right text-small" ></i></a>
				   </h3>
			   </div>
			   <div class="panel-body" id="toc">
					我是内容
			   </div>
		</div>`,
        //固定在右下角的 切换目录按钮
        tocToolbar: `<div class="bottom-toolbar">
			<a class="btn btn-lg btn-link" data-toggle="tooltip"
			      data-placement="top" title="目录">
			    <i class="glyphicon glyphicon-th-list"></i>
			</a>
		</div>`,
        //切换显示目录的按钮
        tocToggle: function ($toc) {
            $(this.tocToolbar).clone().appendTo($("body")).find(".btn").tooltip({"placement": "top"}).on("click", function () {
                $($toc).toggle("slow");
            });
            $("#closeToc", $toc).on("click", function () {
                $($toc).toggle("fast");
            });
        },
        init: function (context) {
            var module = this;
            var generatorToc = !!$.ui;
            $(this.selector, context).each(function (i) {
                var $div = $(this);
                if ($("textarea", $div).size() != 1) {
                    return true; // continue
                }
                var config = $.extend({}, module.config, $div.data())
                // 生成id 以及目录的id
                var id = $div.attr("id") || "editormd-" + i;
                $div.attr("id", id);
                config.markdown = $("textarea", $div).val();

                var $toc;
                if (generatorToc) {
                    var tocId = id + '-toc';
                    $toc = $(module.tocHtml).clone().find("div.panel-body").attr("id", tocId).end();
                    $("body").after($toc);
                    config.tocContainer = "#" + tocId;
                }

                var editor = editormd.markdownToHTML(id, config);
                $div.data("editormd", editor);

                if (generatorToc) {
                    $toc.css("height", $toc.outerHeight()).css("width", $toc.outerWidth());
                    module.tocToggle($toc);
                    $toc.draggable({});
                }

            });
        }
    };

    /* 016 初始化页码 */
    $nojs.pagination = {
        enable: function () {
            return !!$.fn.pager;
        },
        selector: "form ul.pagination",
        init: function (context) {
            $(this.selector, context).pager();
        }

    };
    /**
     * 017 为一个标签绑(一般为a或者btn)定url请求，一般为异步操作
     * 1. 附加参数 包含在这 标签内部的 标签：<i class="action-param" data-key="key" data-value="val" ></i>
     * 2. 可指定为非异步请求 data-asyn="false"
     * 3. 请求前的提示 data-confirm="some message"
     * 4. 请求后的回调 data-after="fnName"
     * 5. 支持未来元素
     * 6. 请求url: data-url="url"
     */
    $nojs.actions = {
        enable: true,
        options: {
            asyn: false,//默认是非异步请求
            url: "",
            type: "get",
            confirm: "",//提示信息
            after: $.noop()
        },
        paramSelector: ".action-param",//绑定参数的元素
        //发起请求的方法
        request: function (type, $e) {
            var module = this;
            var options = $.extend(module.options, {type: type}, $e.data());
            if (!options.url) {
                $.alert("需包含正确的url", "提示");
                return false;
            }
            //如果有提示信息，则先提示 此处使用jquery.confirm
            if (options.confirm) {
                common.confirm(options.confirm, commit, {});
                return false;
            } else {
                commit();
            }

            //开始提交请求
            function commit() {
                var data = getParams();
                //异步提交
                if (options.asyn) {
                    $request.get(options.url, {
                        type: options.type,
                        data: data,
                        confirm: true
                    }, options.after);
                } else {
                    //同步提交  通过构建表单的形式
                    var $form = $('<form></form>').appendTo($("body"));
                    $form.attr("method", options.type).attr("action", options.url);
                    var $input = $("<input type=\"hidden\" class=\"help\"/>");
                    for (var k in data) {
                        var $param = $input.clone().attr("name", k).val(data[k]);
                        $form.append($param);
                    }
                    $form.submit();
                    $form.remove();
                }
            }

            //获得参数
            function getParams() {
                var data = {};
                $(module.paramSelector, $e).each(function () {
                    var k = $(this).data("key");
                    var v = $(this).data("value");
                    data[k] = v;
                });
                return data;
            }
        },
        init: function (context) {
            for (var name in this) {
                var module = this[name];
                if (module && module.init) {
                    module.init(context);
                }
            }
        },
        // get 请求
        get: {
            selector: ".action-get",
            init: function (context) {
                $(document).on("click", this.selector, function () {
                    $nojs.actions.request("get", $(this));
                });
            }
        },
        post: {
            selector: ".action-post",
            init: function (context) {
                $(document).on("click", this.selector, function () {
                    $nojs.actions.request("post", $(this));
                });
            }
        },
        back: {// 返回请求操作 只返回 不刷新
            selector: ".action-back",
            init: function (context) {
                $(this.selector, context).click(function () {
                    windom.history.back();// 返回
                    // window.history.go(-1);//返回+刷新
                });
            }
        }
    };

    //lookup 表单的重置
    $nojs.lookupReset = {
        enable: true,
        selector: {
            form: "form.lookup",//要重置的表单
            reset: "button.reset",//触发的按钮
            control: ":input"//要重置的对象
        },
        init: function (context) {
            var module = this;
            var $form = $(module.selector.form, context);
            $(module.selector.reset, $form).on("click", function () {
                $(module.selector.control, $form).val("");
                $form.submit();
            });
        }
    };

    $nojs.cleanInputGroup = {
        enable: true,
        selector: "div.input-group .clean",
        init: function (context) {
            $(this.selector, context).each(function () {
                $(this).on("click", function (event) {
                    $(this).siblings(":input").val("");
                    event.stopPropagation();
                });
            })
        }
    };


    windom.$nojs = $nojs;

})(window, jQuery);
$(document).ready(function () {
    console.info("into nojs");
    window.$nojs(document);
});
